package com.cyh;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test01 {
    public static void main(String arg[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //只知道类和方法的字符串名字，而不知道类名的时候如何办
        Class cls = Class.forName("com.cyh.Cat");
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass());
        Method method1 = cls.getMethod("hi");
        method1.invoke(o);

        //反射的作用：通过外部文件配置，在不修改源码情况下，来控制程序，也符合设计模式ocp原则（开闭原则：不修改源码，扩容功能）


        //getField 不能得到私有的属性，
        Field nameField = cls.getField("age");
        System.out.println(nameField.get(o));

        Constructor constructor = cls.getConstructor();
        System.out.println(constructor);
        Constructor constructor2 = cls.getConstructor(String.class); //括号里面是参数的类型
        System.out.println(constructor2);

        //获取class对象的方式: 5中方式
        Class cls1 = Class.forName("com.cyh.Cat"); //编译阶段加载， 多用于xml配置文件获得类的场景（前提：知道全类名）
        Class cls2 = Cat.class; //类加载阶段加载，最安全可靠、性能最高的方式（前提：已知具体的类，多用于参数传递）
        Cat cat = new Cat();
        Class cls3 = cat.getClass(); //运行阶段加载，平时的用法，
        //通过类加载器来获取到类的class对象
        ClassLoader classLoader = cat.getClass().getClassLoader();
        Class cls4 = classLoader.loadClass("com.cyh.Cat");
        //基本类型获得class类
        Class cls5 = int.class;
        //包装类获取类对象
        Class cls6 = Integer.TYPE;


        //哪些类型有Class对象
        //类、内部类、静态类、内部类、接口、数组、enum、annotation注解、基本数据类型、void
        Class cls7 = Integer[].class;
        Class cls8 = Integer[][].class;
        Class cls9 = Deprecated.class; //注解类
        Class cls10 = Thread.State.class; //枚举
        Class cls11 = void.class;
        Class cls12 = Class.class;

        //Class类的结构信息
        //1、getName:获取全类名
        //2、getSimpleName: 获取简单的类名
        // 3、getFields: 获取所有public修饰的属性，包括本类以及父类的
        // 4、getDeclaredFields:获取本类的所有属性,包括public和非public的
        // 5、getMethods: 获取所有public修饰的方法，包括本类以及父类的
        // 6、getDeclaredMethods: 获取本类中的所有方法
        // 7、getConstructors: 获取本类所有public修饰的构造函数，不包括父类的
        // 8、getDeclaredConstructors: 获取本类中的所有构造函数
        // 9、getPackage: 以package形式返回包信息
        // 10、getSuperClass: 以class形式返回父类信息
        // 11、getInterfaces: 以Class[]形式返回接口信息
        // 12、getAnnotations: 以Annotation[]形式返回注解信息
        //13、newInstance()或newInstance(Object object) 新建对象
        //14、getClassLoader: 返回该类的类加载器

        //Field类的结构信息：
        nameField.getName();
        //1、getModifiers:以int返回修饰符， 默认修饰符：0， public:1, private:2,protected:4, static:8, final:16
        //2、getType: 以Class形式返回类型
        //3、getName: 返回属性名

        //Method类的结构信息
        method1.getName();
        //1、getModifiers:以int返回修饰符， 默认修饰符：0， public:1, private:2,protected:4, static:8, final:16
        //2、getReturnType:以Class形式获取返回类型
        //3、getName:返回方法名
        //4、getParameterTypes:以Class[]返回参数类型数组

        //通过反射创建对象
        //1、方式一：调用类中的public修饰的无参构造器
        //2、方式二：调用类中的指定构造器
        //3、Class类相关涉及创建对象的方法
        //   newInstance:调用类中的无参构造器，获取对应类的对象
        //   getConstructor(Class...clazz):根据参数列表，获取对应的构造器对象
        //   getDeclaredConstructor(Class...clazz):根据参数列表，获取对应的构造器对象
        //4、 构造器对象类Constructor类涉及创建对象的相关方法
        //   setAccessible:爆破，私有的构造器或者方法访问会报错，构造器或方法对象.setAccessible(true); 就可以访问私有的了。
        //   newInstance(Object...obj): 调用构造器来构造对象。
        //方式一：
        Class cls13 = Class.forName("com.cyh.Cat");
        //Object cat13 = cls13.newInstance();
        Cat cat13 = (Cat)cls13.newInstance();//相当于调用无参构造器构建对象

        //方式二：
        Class cls14 = Class.forName("com.cyh.Cat");
        //Constructor constructor14 = cls14.getConstructor(String.class, int.class); //只能获取public的参数构造器
        Constructor constructor14 = cls14.getDeclaredConstructor(String.class, int.class); //获取所有对应的参数构造器
        constructor14.setAccessible(true); //爆破，可以访问私有方法
        //Object cat14 = constructor14.newInstance("aaa", "18");
        Cat cat14 = (Cat)constructor14.newInstance("aaa", 18);


        //通过反射访问类中的属性
        //1、根据属性名获取Field对象：Field f = clazz对象.getDeclaredField(属性名);
        //2、爆破：f.setAccessible(true); //可以访问私有属性
        //3、 访问： f.set(o, 值); f.get(o);//o表示对象，
        //4、注意：如果是  静态属性  则set和get中的参数o, 可以写成null
        Class cls15 = Class.forName("com.cyh.Cat");
        //Field field15 = cls15.getField("name");//只获取public的
        Field field15 = cls15.getDeclaredField("name");//可以获取本类所有的
        Cat cat15 = (Cat)cls15.newInstance();
        field15.setAccessible(true);
        field15.set(cat15, "bbb");
        String str15 = (String)field15.get(cat15);
        System.out.println(str15);


        //通过反射访问类中的方法
        //1、m= clazz对象.getMethod(方法名，XX.class)或者clazz对象.getDeclaredMethod(方法名，XX.class)获取方法
        //2、获取对象： Object o = class.newInstance();
        //3、爆破：m.setAccessible(true);
        //4、访问：Object returnValue = m.invoke(o, 实参列表);
        //5、注意：如果是 静态方法， 在invoke的参数o, 可以写成null
        Class cls16 = Class.forName("com.cyh.Cat");
        //Method method16 = cls16.getMethod("hi", String.class);
        Method method16 = cls16.getDeclaredMethod("hi2", String.class);
        Object object16 = cls16.newInstance();
        method16.setAccessible(true);
        Object str16 = method16.invoke(object16, "哈哈哈哈2");
        System.out.println(str16);


        /*Class cls = this.getClass().getSuperclass();
        Method method = cls.getDeclaredMethod("resetRotation");
        method.setAccessible(true);
        method.invoke(this);

        Field field = this.getClass().getSuperclass().getDeclaredField("mGestureCropImageView");
        field.setAccessible(true);
        GestureCropImageView gestureCropImageView = (GestureCropImageView) field.get(this);

        gestureCropImageView.zoomInImage(mInitScale);*/



        //注解: 作用是给其他程序和编译器看的。通过反射来读取注解
        //@Target 表示使用范围
        //@Retention 用于描述注解的生命周期 SOURCE < CLASS < RUNTIME， 一般是RUNTIME
        //@Document 说明该注解将被包含在javadoc中
        //@Inherited 说明子类可以继承父类中的该注解
        /**
         * 如何定义和使用注解
         * 1、@interface用来声明一个注解，格式： public @interface 注解名{定义内容}
         * 2、其中的每一个方法实际上是声明了一个配置参数
         * 3、方法的名称就是参数的名称
         * 4、返回值得类型就是参数的类型（返回值只能是基本类型、Class、String、enum, 或者他们得数组）
         * 5、可以通过default来声明参数的默认值
         * 6、如果只有一个参数成员，一般参数名为value
         * 7、注解元素必须要有值，我们定义注解元素时，经常使用空字符串，0作为默认值
         * */
    }
}
